iT邦幫忙

2024 iThome 鐵人賽

DAY 20
0
Python

用 Python 打造你的 Discord BOT系列 第 20

[Day 20] 錯誤處理

  • 分享至 

  • xImage
  •  

延續昨天主題,今天接著介紹錯誤處理。

進度

昨天介紹了日誌系統,今天來介紹與它息息相關的錯誤處理。

進度圖跟昨天一樣 XD

錯誤處理

其實 discord.py 已經幫忙處理很多錯誤了,只不過,有時候我們還是會希望可以稍微插手一下,讓 Discord BOT 可以呈現一些錯誤提示;或者是使用昨天介紹的日誌系統,把需要的資訊紀錄都記錄下來。

接下來,會盡量把前面幾種不同的功能的錯誤處理都展示一下。

Clientevent

import discord

intents = discord.Intents.default()
intents.message_content = True

client = discord.Client(intents=intents)

@client.event
async def on_message(message: discord.Message):
    if message.author == client.user:
        return

    if message.content.startswith("ping"):
        a = 1 / 0  # 這邊會出錯
        await message.channel.send("Pong!")

@client.event
async def on_error(event, *args, **kwargs):
    print(f"發生錯誤: {event} {args} {kwargs}")
    if event == "on_message":
        await args[0].channel.send(f"{event} 發生錯誤了!")

bot.run('token')

當對事件的處理發生錯誤時,就可以被 on_error 捕捉到。雖然可以知道是哪一個事件觸發的,但詳細的錯誤訊息需要另外用 sys.exc_info() 才能拿到了。

Botcommand

import discord
from discord.ext import commands

intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='>', intents=intents)

@bot.command()
async def ping(ctx):
    a = 1 / 0  # 這邊會出錯
    await ctx.send('pong')

# catch command error
@bot.event
async def on_command_error(ctx: commands.Context, error: commands.CommandError):
    await ctx.send(f"發生錯誤了: {error}")

bot.run('token')

Bot 可以用自己的 on_command_error 捕捉 bot.command 內的錯誤,不用跟 on_error 擠在一起。

CommandTree 內的 command

這邊用 Slash Command 作為例子

# ...

@bot.tree.command()
async def ping(interaction: discord.Interaction):
    a = 1 / 0  # 這邊會出錯
    await interaction.response.send_message("Pong!")

@bot.tree.error
async def on_error(interaction: discord.Interaction, error: Exception) -> None:
    await interaction.response.send_message(f"Oops! {error}", ephemeral=True)

# ...

CommandTree 這個容器內的應用指令發生錯誤時,可以用 on_error 來捕捉錯誤。如果需要知道是哪個應用指令出錯,可以用 discord.Interaction.command 取得。

元件的 callback

# ...

async def on_click(interaction: discord.Interaction):
    a = 1 / 0  # 這邊會出錯
    await interaction.response.send_message("剛剛點擊了按鈕")

async def on_btn_error(
    interaction: discord.Interaction, error: Exception, item: discord.ui.Item
) -> None:
    await interaction.response.send_message(f"Oops! {error}", ephemeral=True)

@bot.command()
async def ping(ctx: commands.Context):
    view = discord.ui.View()
    btn = discord.ui.Button(label="我是按鈕")
    btn.callback = on_click
    view.add_item(btn)
    view.on_error = on_btn_error
    await ctx.send("點擊下方按鈕", view=view)

# ...

如果要捕捉 View 內的錯誤,可以用 View.on_error 來設定。有需要的話,可以從 Item 來取得發生錯誤的元件的相關資訊。

Modal

# ...

class Questionnaire(discord.ui.Modal, title="Questionnaire Response"):
    name = discord.ui.TextInput(label='Name')
    answer = discord.ui.TextInput(label='Answer', style=discord.TextStyle.paragraph)

    async def on_submit(self, interaction: discord.Interaction):
        a = 1 / 0  # 這邊會出錯
        await interaction.response.send_message(
            f"Thanks for your response, {self.name}!", ephemeral=True
        )
    
    async def on_error(self, interaction: discord.Interaction, error: Exception) -> None:
        await interaction.response.send_message(f"Oops! {error}", ephemeral=True)

# ...

如果在 on_submit 發生錯誤,則會被 on_error 捕捉到。由於 Modal 一定要用 subclass 的方式,所以就直接把 on_error 放在 class 之中就好了。同時,這樣管理這些程式碼也可以提高可維護性。

Task

import discord
import logging
from discord.ext import commands, tasks

intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='', intents=intents)
logger = logging.getLogger("discord")

@bot.command()
async def ping(ctx):
    slow_count.start()
    await ctx.send('pong')

@tasks.loop(seconds=1.0, count=5)
async def slow_count():
    a = 1 / (3 - slow_count.current_loop)  # 第 4 次會出錯
    await ctx.send(slow_count.current_loop)

@slow_count.after_loop
async def after_slow_count():
    logger.info('done!')

@slow_count.error
async def on_slow_count_error(error: Exception):
    logger.error(f"{error}")

bot.run('token')

Discord BOT 在執行第 4 次的時候發生錯誤,導致只有傳送 0 到 2,後面剩餘的次數就沒繼續執行了。

雖然 task 中斷了,但是 after_loop 的內容依然有執行 (紀錄 Log)。而且,發生錯誤的那次,有被 error 捕捉到。

小結

今天介紹了各種情境下要如何去捕捉錯誤,之後不論是要像這篇文章一樣,讓 Discord BOT 把錯誤訊息回傳出來;或是使用昨天介紹的日誌系統,把這些錯誤訊息記錄下來,都是可以的做法。


上一篇
[Day 19] 日誌系統
下一篇
[Day 21] bot 指令管理 (一):Cog
系列文
用 Python 打造你的 Discord BOT31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言